#ifndef CNC_BASE_DATABASE_IMPLEMENTATION_H
#define CNC_BASE_DATABASE_IMPLEMENTATION_H

#include "parameter_.h"
// provided interfaces
#include "persistentdata.h"
// basic component model
#include <bcm/bcm.h>
#include <map>
#include <string>
#include <fstream>
// debug
#include <iostream>

namespace cnc {

	namespace base {

		class DatabaseImpl : public bcm::Identifier<PersistentData>
		{
		protected:
			typedef std::map<std::string, ParameterImpl> Container_;
			Container_ parameter_;
			Container_::iterator nameIt_;
		protected:
			DatabaseImpl() : bcm::Identifier<PersistentData>("pdata", "Persistent Data"),
				nameIt_(parameter_.begin())
			{
			}
			PersistentData *getPersistentData()
			{
				return static_cast<PersistentData *>(this);
			}
		protected: // PersistentData
			virtual bool load(const char *filename)
			{
				parameter_.clear();
				std::ifstream file(filename, std::ios::binary);
				std::string id;
				char typeID;
				if (file.good() == false)
					return false;
				while (file.good()) {
					getline(file, id, '\0');
					if (file.eof())
						continue;
					file.read(&typeID, sizeof(char));
					switch (typeID) {
					case DT_INT8:
						{
							int8_t int8_value;
							file.read(reinterpret_cast<char *>(&int8_value), sizeof(int8_t));
							parameter_[id] = ParameterImpl();
							parameter_[id].setDataType(DT_INT8);
							parameter_[id].set(int8_value);
						}
						break;
					case DT_UINT8:
						{
							uint8_t uint8_value;
							file.read(reinterpret_cast<char *>(&uint8_value), sizeof(uint8_t));
							parameter_[id] = ParameterImpl();
							parameter_[id].setDataType(DT_UINT8);
							parameter_[id].set(uint8_value);
						}
						break;
					case DT_INT16:
						{
							int16_t int16_value;
							file.read(reinterpret_cast<char *>(&int16_value), sizeof(int16_t));
							parameter_[id] = ParameterImpl();
							parameter_[id].setDataType(DT_INT16);
							parameter_[id].set(int16_value);
						}
						break;
					case DT_UINT16:
						{
							uint16_t uint16_value;
							file.read(reinterpret_cast<char *>(&uint16_value), sizeof(uint16_t));
							parameter_[id] = ParameterImpl();
							parameter_[id].setDataType(DT_UINT16);
							parameter_[id].set(uint16_value);
						}
						break;
					case DT_INT32:
						{
							int32_t int32_value;
							file.read(reinterpret_cast<char *>(&int32_value), sizeof(int32_t));
							parameter_[id] = ParameterImpl();
							parameter_[id].setDataType(DT_INT32);
							parameter_[id].set(int32_value);
						}
						break;
					case DT_UINT32:
						{
							uint32_t uint32_value;
							file.read(reinterpret_cast<char *>(&uint32_value), sizeof(uint32_t));
							parameter_[id] = ParameterImpl();
							parameter_[id].setDataType(DT_UINT32);
							parameter_[id].set(uint32_value);
						}
						break;
					case DT_INT64:
						{
							int64_t int64_value;
							file.read(reinterpret_cast<char *>(&int64_value), sizeof(int64_t));
							parameter_[id] = ParameterImpl();
							parameter_[id].setDataType(DT_INT64);
							parameter_[id].set(int64_value);
						}
						break;
					case DT_UINT64:
						{
							uint64_t uint64_value;
							file.read(reinterpret_cast<char *>(&uint64_value), sizeof(uint64_t));
							parameter_[id] = ParameterImpl();
							parameter_[id].setDataType(DT_UINT64);
							parameter_[id].set(uint64_value);
						}
						break;
					case DT_FLOAT32:
						{
							float float32_value;
							file.read(reinterpret_cast<char *>(&float32_value), sizeof(float));
							parameter_[id] = ParameterImpl();
							parameter_[id].setDataType(DT_FLOAT32);
							parameter_[id].set(float32_value);
						}
						break;
					case DT_FLOAT64:
						{
							double float64_value;
							file.read(reinterpret_cast<char *>(&float64_value), sizeof(double));
							parameter_[id] = ParameterImpl();
							parameter_[id].setDataType(DT_FLOAT64);
							parameter_[id].set(float64_value);
						}
						break;
					}
				}
				return true;
			}
			virtual bool save(const char *filename)
			{
				std::ofstream file(filename, std::ios::binary);
				Container_::iterator it;
				char typeID;
				for (it = parameter_.begin(); it != parameter_.end(); it++) {
					file << it->first.c_str() << '\0';
					typeID = it->second.getDataType();
					file.write(&typeID, sizeof(char));
					switch (it->second.getDataType()) {
					case DT_INT8:
						{
							int8_t int8_value;
							it->second.get(&int8_value);
							file.write(reinterpret_cast<char *>(&int8_value), sizeof(int8_t));
						}
						break;
					case DT_UINT8:
						{
							uint8_t uint8_value;
							it->second.get(&uint8_value);
							file.write(reinterpret_cast<char *>(&uint8_value), sizeof(uint8_t));
						}
						break;
					case DT_INT16:
						{
							int16_t int16_value;
							it->second.get(&int16_value);
							file.write(reinterpret_cast<char *>(&int16_value), sizeof(int16_t));
						}
						break;
					case DT_UINT16:
						{
							uint16_t uint16_value;
							it->second.get(&uint16_value);
							file.write(reinterpret_cast<char *>(&uint16_value), sizeof(uint16_t));
						}
						break;
					case DT_INT32:
						{
							int32_t int32_value;
							it->second.get(&int32_value);
							file.write(reinterpret_cast<char *>(&int32_value), sizeof(int32_t));
						}
						break;
					case DT_UINT32:
						{
							uint32_t uint32_value;
							it->second.get(&uint32_value);
							file.write(reinterpret_cast<char *>(&uint32_value), sizeof(uint32_t));
						}
						break;
					case DT_INT64:
						{
							int64_t int64_value;
							it->second.get(&int64_value);
							file.write(reinterpret_cast<char *>(&int64_value), sizeof(int64_t));
						}
						break;
					case DT_UINT64:
						{
							uint64_t uint64_value;
							it->second.get(&uint64_value);
							file.write(reinterpret_cast<char *>(&uint64_value), sizeof(uint64_t));
						}
						break;
					case DT_FLOAT32:
						{
							float float32_value;
							it->second.get(&float32_value);
							file.write(reinterpret_cast<char *>(&float32_value), sizeof(float));
						}
						break;
					case DT_FLOAT64:
						{
							double float64_value;
							it->second.get(&float64_value);
							file.write(reinterpret_cast<char *>(&float64_value), sizeof(double));
						}
						break;
					}
				}
				return false;
			}
			virtual void clear()
			{
				parameter_.clear();
			}
			virtual const char *getFirstParameterName()
			{
				nameIt_ = parameter_.begin();
				if (parameter_.empty())
					return 0;
				return nameIt_->first.c_str();
			}
			virtual const char *getNextParameterName()
			{
				nameIt_++;
				if (nameIt_ == parameter_.end())
					return 0;
				return nameIt_->first.c_str();
			}
			virtual bool addParameter(const char *paramID, DataTypeID dataType)
			{
				Container_::iterator it = parameter_.find(std::string(paramID));
				if (it != parameter_.end())
					return false;
				parameter_[std::string(paramID)] = ParameterImpl();
				parameter_[std::string(paramID)].setDataType(dataType);
				return true;
			}
			virtual bool removeParameter(const char *paramID)
			{
				Container_::iterator it = parameter_.find(std::string(paramID));
				if (it == parameter_.end())
					return false;
				parameter_.erase(paramID);
				return true;
			}
			virtual Parameter *getParameter(const char *paramID)
			{
				Container_::iterator it = parameter_.find(std::string(paramID));
				if (it == parameter_.end())
					return 0;
				return &parameter_[std::string(paramID)];
			}
		public:
			static DatabaseImpl &DatabaseImpl::instance()
			{
				static DatabaseImpl object;
				return object;
			}
		};

	} // namespace base

} // namespace cnc

#endif
